#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/core/make_intrusive_handle.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_properties.h>
#include <vex.tests/mstest/tests.core/user_contract.h>
#include <vex.tests/mstest/tests.core/user_base.h>
#include <vex.tests/mstest/tests.core/user_type_traits.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests { namespace make_intrusive_handle {
    
    namespace vc = vex::core;
    namespace vcc = vex::core::contract;
    namespace vci = vex::core::implementation;

    TEST_CLASS(make_intrusive_handle)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.core.make_intrusive_handle")
        END_TEST_CLASS_ATTRIBUTE();
    public:
        void make_intrusive_handle_delivers_a_valid_handle_impl()
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            if (auto c = vc::make_intrusive_handle<user_base>()) {
                handle_properties::verify_sharing(c.get(), vc::shared_synchronized_tag());

                Assert::AreEqual(user_base_traits::get<int>(user_base::get_defaults()),
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template <class TArg1>
        void make_intrusive_handle_delivers_a_valid_handle_impl(TArg1&& p_arg1)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            if (auto c = vc::make_intrusive_handle<user_base>( 
                std::forward<TArg1>(p_arg1))) 
            {
                handle_properties::verify_sharing(c.get(), vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(user_base_traits::get<double>(user_base::get_defaults()),
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template <class TArg1, class TArg2>
        void make_intrusive_handle_delivers_a_valid_handle_impl(TArg1 && p_arg1, TArg2 && p_arg2)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            if (auto c = vc::make_intrusive_handle<user_base>( 
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2))) 
            {
                handle_properties::verify_sharing(c.get(), vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(user_base_traits::get<std::string>(user_base::get_defaults()),
                    std::string(c->get_value(alias<char const*>()))); 
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        template <class TArg1, class TArg2, class TArg3>
        void make_intrusive_handle_delivers_a_valid_handle_impl(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            if (auto c = vc::make_intrusive_handle<user_base>( 
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3))) 
            {
                handle_properties::verify_sharing(c.get(), vc::shared_synchronized_tag());

                Assert::AreEqual(p_arg1,
                    c->get_value(alias<int>())); 
                Assert::AreEqual(p_arg2,
                    c->get_value(alias<double>())); 
                Assert::AreEqual(std::string(p_arg3),
                    std::string(c->get_value(alias<char const*>()))); 
            } else Assert::Fail(L"Handle could not be allocated!");
        }

        TEST_METHOD(make_intrusive_handle_delivers_a_valid_handle_for_nullary_form)
        {
            this->make_intrusive_handle_delivers_a_valid_handle_impl();
            this->make_intrusive_handle_delivers_a_valid_handle_impl();
            this->make_intrusive_handle_delivers_a_valid_handle_impl();
        }

        TEST_METHOD(make_intrusive_handle_delivers_a_valid_handle_for_unary_form)
        {
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42);

            int t_lvalue = 42;
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_lvalue);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_lvalue);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_lvalue);
        }

        TEST_METHOD(make_intrusive_handle_delivers_a_valid_handle_for_binary_form)
        {
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1);

            int t_i = 42;
            double t_d = 24.1;
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d);
        }

        TEST_METHOD(make_intrusive_handle_delivers_a_valid_handle_for_ternary_form)
        {
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1, "josephine");
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1, "josephine");
            this->make_intrusive_handle_delivers_a_valid_handle_impl(42, 24.1, "josephine");

            int t_i = 42;
            double t_d = 24.1;
            std::string t_s = "josephine";
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d, t_s);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d, t_s);
            this->make_intrusive_handle_delivers_a_valid_handle_impl(t_i, t_d, t_s);
        }
    };
}}